#include "stofac.h"
#include "bigmcallback.h"

/* Solve call back functions */
int CPXPUBLIC
DZcallbacksetbigm      
(CPXCENVptr   env,
 void         *cbdata,
 int          wherefrom,
 void         *cbhandle,
 int          *useraction_p
 )   
{
  CPXLPptr nodelp;
  BIGMINFOptr minfo_p = (BIGMINFOptr) cbhandle;
  int i, j, k;   // counters
  PROBINFO pinfo;
  PROBINFOptr pinfo_p = &pinfo;

  /* 
     The local variables can be part of the minfo struct
     for the moment we leave them here. But we will remove them 
     later for performance
  */
  int nodecount;
  int status;

  int xind;
  int yind;
  int itercount=0;

  double bigmval;
  double tol = 0.0001;

  /* Copy pointers from minfo to local pointers */
  int *bigmrowind = minfo_p->bigmrowind;
  int *bigmcolind = minfo_p->bigmcolind;
  int *bigmchgind = minfo_p->bigmchgind;
  int *flagPtr    = minfo_p->flagPtr;
  int *lastnodePtr = minfo_p->lastnode;
  int *loopind = minfo_p->loopind;
  int nbigm = minfo_p->nbigm;

  *useraction_p = CPX_CALLBACK_DEFAULT;

  /* Get pointer of the node lp */
  status = CPXgetcallbacknodelp (env, cbdata, wherefrom, &nodelp);
  if ( status )  goto TERMINATE;

  /* Create problem info structure */
  status = DZcreateprobinfo(env, nodelp, pinfo_p);
  if ( status )  goto TERMINATE;

  /* Find out what nodes is being processed */
  status = CPXgetcallbackinfo (env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODE_COUNT, &nodecount);
  if ( status )  goto TERMINATE;

  /* Solve the node lp using different method*/
  if (nodecount < 1) status = CPXprimopt(env, nodelp);
    //status = CPXhybbaropt(env, nodelp, 0);

  else                 status = CPXdualopt(env, nodelp);

  //  if (nodecount == 1) CPXwriteprob(env, nodelp, "stofac_node1.lp", NULL);

  /* Get problem information */
  DZgetprobinfo(env, nodelp, pinfo_p);
  
  /* Reset bigm change index */
  for(i = 0; i < nbigm; i++) bigmchgind[i] = 0;

  if(*loopind == 3)
    printf("%6s %6s %6s %6s %6s %10s %10s %10s %10s %10s %10s %10s\n",
	 "node#", "rid", "rstat", "cid", "cstat", 
	 "x", "y", "ylb", "yub", "slack", "rhs", "bigm");

  /* Big m checking routine */
  if(nodecount > -1)
    {
      for(i = 0; i < nbigm; i++)
	{
	  xind = (bigmrowind[i] - 1)%(minfo_p->ni);
	  yind = bigmcolind[i] - (minfo_p->ni);
	  status = CPXgetcoef(env, nodelp, bigmrowind[i], bigmcolind[i], &bigmval);


	  if ( ( (pinfo_p->rstat)[bigmrowind[i]] == CPX_AT_LOWER) && 
	    // ((y[yind] == CPX_AT_UPPER)))
	    //	       ( fabs( (pinfo_p->y)[yind] - 1.0) <= tol))
	       ((pinfo_p->y)[yind] > 1.0 - tol))
	    //	       ( (pinfo_p->y)[yind] > tol)
	    //	       ( ((pinfo_p->x)[xind] > tol) ||

	    
	    {
	      bigmchgind[i] = 1;
	      if(*loopind == 3)
		printf("%6i %6i %6i %6i %6i %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f ***\n", 
		       nodecount, 
		       bigmrowind[i], 
		       (pinfo_p->rstat)[bigmrowind[i]], 
		       bigmcolind[i], 
		       (pinfo_p->cstat)[bigmcolind[i]], 
		       (pinfo_p->x)[xind], 
		       (pinfo_p->y)[yind], 
		       (pinfo_p->ylb)[yind], 
		       (pinfo_p->yub)[yind],
		       (pinfo_p->slack)[bigmrowind[i]], 
		       (pinfo_p->rhs)[i+1], 
		       bigmval);

	    }
	  else
	    {
	      itercount++;

	      if(*loopind == 3)
		printf("%6i %6i %6i %6i %6i %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f\n", 
		       nodecount, 
		       bigmrowind[i], 
		       (pinfo_p->rstat)[bigmrowind[i]], 
		       bigmcolind[i], 
		       (pinfo_p->cstat)[bigmcolind[i]], 
		       (pinfo_p->x)[xind], 
		       (pinfo_p->y)[yind], 
		       (pinfo_p->ylb)[yind], 
		       (pinfo_p->yub)[yind],
		       (pinfo_p->slack)[bigmrowind[i]], 
		       (pinfo_p->rhs)[i+1], 
		       bigmval);

	    }
	}


      if( (itercount == nbigm) && (*loopind == 1) )
	{
	  *useraction_p = CPX_CALLBACK_SET;    //Go to the next node
	  *flagPtr = 1;
	  //	  printf("Node %i\n", nodecount);
	  /*
	  printf("%6s %6s %6s %6s %6s %10s %10s %10s %10s %10s %10s %10s\n",
		 "node#", "rid", "rstat", "cid", "cstat", 
		 "x", "y", "ylb", "yub", "slack", "rhs", "bigm");
	  */
	  for(i = 0; i < nbigm; i++)
	    {
	      if ( 0
		  //(fabs(y[yind] - 1.0) <= tol)
		  //(rstat[bigmrowind[i]] == CPX_AT_LOWER)
		   )
		{
		  xind = (bigmrowind[i] - 1)%(minfo_p->ni);
		  yind = bigmcolind[i] - (minfo_p->ni);	  
		  status = CPXgetcoef(env, nodelp, bigmrowind[i], bigmcolind[i], &bigmval);
		  /*
		  printf("%6i %6i %6i %6i %6i %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f %10.6f\n", 
			 nodecount, bigmrowind[i], rstat[bigmrowind[i]], bigmcolind[i], cstat[bigmcolind[i]], 
			 x[xind], y[yind], ylb[yind], yub[yind],
			 slack[bigmrowind[i]], rhs[i], bigmval);
		  */
		}
	    }

	  goto TERMINATE;
	}
      else if(*loopind == 1)
	{
	  *flagPtr = 0;
	  *useraction_p = CPX_CALLBACK_FAIL;   //Stop the mipo
	  *lastnodePtr = nodecount;
	  goto TERMINATE;
	}
      else //single 
	{
	  *useraction_p = CPX_CALLBACK_SET;    //Go to the nex
	}
    }

 TERMINATE:
  DZdeleteprobinfo(pinfo_p);
  return status;
}

/* Solve call back functions */
int CPXPUBLIC
DZcallbackcheckconbds      
(CPXCENVptr   env,
 void         *cbdata,
 int          wherefrom,
 void         *cbhandle,
 int          *useraction_p
 )   
{
  CPXLPptr nodelp;
  CONINFOptr cp = (CONINFOptr) cbhandle;
  int i, j, k;   // counters
  PROBINFO pinfo;
  PROBINFOptr pp = &pinfo;

  int nodecount;
  int status;
  int itercount=0;
  double tol = 0.0001;
  /* Copy pointers from coninfo to local pointers */


  int *conind       = cp->conind;
  int *conchgbdsind = cp->conchgbdsind;
  int *flagPtr      = cp->flagPtr;
  int *loopind      = cp->loopind;
  int     ncon      = cp->ncon;
  int *lastnodePtr  = cp->lastnode;
  //  printf("%i %i\n", conind, conchgbdsind);
  *useraction_p = CPX_CALLBACK_DEFAULT;

  /* Get pointer of the node lp */
  status = CPXgetcallbacknodelp (env, cbdata, wherefrom, &nodelp);
  if ( status )  goto TERMINATE;

  /* Create problem info structure */
  status = DZcreateprobinfo(env, nodelp, pp);

  /* Find out what nodes is being processed */
  status = CPXgetcallbackinfo (env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODE_COUNT, &nodecount);

  /* Solve the node lp using different method*/
  if (nodecount < 1) status = CPXprimopt(env, nodelp);
  else               status = CPXdualopt(env, nodelp);

  /* Get problem information */
  pp->ncon = ncon;
  DZgetprobinfo(env, nodelp, pp);

  if(*loopind == 3)
    printf("%6s %6s %6s %6s %6s %10s %10s %10s %10s %10s %10s %10s\n",
	   "node#", "rid", "rstat", "cid", "cstat", 
	   "x", "y", "ylb", "yub", "slack", "rhs", "bigm");

  /* Reset con indices */
  for(i = 0; i < ncon; i++) conchgbdsind[i] = 0;

  /* Bound checking routine */
  for(i = 0; i < ncon; i++)
    {
      if ( (pp->cstat)[i] == CPX_AT_LOWER ) 
	conchgbdsind[i] = 1;
      else  itercount++;
    }

  if ( itercount == ncon)
    {
      *useraction_p = CPX_CALLBACK_SET;    //Go to the next node
      *flagPtr = 1;
      goto TERMINATE;
    }
  else
    {
      *useraction_p = CPX_CALLBACK_FAIL;   //Stop the mipo
      *flagPtr = 0;
      *lastnodePtr = nodecount;
      goto TERMINATE;
    }


 TERMINATE:
  DZdeleteprobinfo(pp);
  return status;
}

int DZcreateprobinfo(CPXCENVptr env, CPXLPptr lp, PROBINFOptr pinfo_p)
{
  pinfo_p->nrows =  CPXgetnumrows(env, lp);
  pinfo_p->ncols =  CPXgetnumcols(env, lp);
  //  pinfo_p->ncon  =  CPXgetnumbin (env, lp);
  pinfo_p->nbigm =  pinfo_p->nrows -1 ;
  int nrows = pinfo_p->nrows;
  int ncols = pinfo_p->ncols;

  //  pinfo_p->nbin  =  ;
  //  pinfo_p->ncon  =  pinfo_p->ncols - pinfo_p->nbin;
  pinfo_p->a =     (double *)malloc(ncols * sizeof(double));
  pinfo_p->alb =   (double *)malloc(ncols * sizeof(double));
  pinfo_p->aub =   (double *)malloc(ncols * sizeof(double));
  pinfo_p->cstat =   (int *) malloc (ncols * sizeof(int));

  pinfo_p->rhs =   (double *)malloc(nrows * sizeof(double));
  pinfo_p->slack = (double *)malloc(nrows * sizeof(double));
  pinfo_p->rstat =   (int *) malloc (nrows * sizeof(int));
  return 0;
}

int DZgetprobinfo(CPXCENVptr env, CPXLPptr nodelp, PROBINFOptr pinfo_p)
{
  int status;
  /* Recover the MIP information */
  pinfo_p->nbigm =  pinfo_p->nrows -1 ;
  //  pinfo_p->nbin  =  minfo_p->ns;
  //  pinfo_p->ncon  =  pinfo_p->ncols - pinfo_p->nbin;

  /* Set pointer location for x and y */
  pinfo_p->x   = pinfo_p->a;
  pinfo_p->y   = pinfo_p->a  + pinfo_p->ncon;
  pinfo_p->xlb = pinfo_p->alb;
  pinfo_p->ylb = pinfo_p->alb + pinfo_p->ncon;
  pinfo_p->xub = pinfo_p->aub;
  pinfo_p->yub = pinfo_p->aub + pinfo_p->ncon;

  /* Get solution infomation from node lp */
  status = CPXgetx (env, nodelp, pinfo_p->a, 0, pinfo_p->ncols-1);     //get value of x
  status = CPXgetlb(env, nodelp, pinfo_p->alb, 0, pinfo_p->ncols-1);
  status = CPXgetub(env, nodelp, pinfo_p->aub, 0, pinfo_p->ncols-1);
  status = CPXgetslack(env, nodelp, pinfo_p->slack, 0, pinfo_p->nrows-1);
  status = CPXgetrhs(env, nodelp, pinfo_p->rhs, 0, pinfo_p->nrows-1);

  /* Get the bases */
  status = CPXgetbase (env,  nodelp, pinfo_p->cstat, pinfo_p->rstat);
  return 0;
}

void DZdeleteprobinfo(PROBINFOptr pinfo_p)
{
  free(pinfo_p->a);
  free(pinfo_p->rhs);
  free(pinfo_p->alb);
  free(pinfo_p->aub);
  free(pinfo_p->slack);
  free(pinfo_p->rstat);
  free(pinfo_p->cstat);
  return;
}
